<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="UTF-8">
  <title>数电发票批量合并打印</title>
  <!-- pdf.js -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js"></script>
  <!-- jsPDF -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
  <!-- Font Awesome -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
  <style>
   * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }
    
    body {
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
      background: linear-gradient(135deg, #f5f7fa 0%, #e4edf5 100%);
      color: #333;
      line-height: 1.6;
      padding: 20px;
      min-height: 100vh;
    }
    
    .container {
      max-width: 1600px;
      margin: 0 auto;
      display: flex;
      gap: 20px;
    }
    
    header {
      text-align: center;
      margin-bottom: 30px;
      padding: 10px;
      background: linear-gradient(90deg, #1a6dcc, #2c87e5);
      color: white;
      border-radius: 10px;
      box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
    }
    
    header h1 {
      font-size: 2.2rem;
      margin-bottom: 10px;
    }
    
    header p {
      font-size: 1.1rem;
      opacity: 0.9;
    }
    
    .panel {
      background: white;
      border-radius: 10px;
      box-shadow: 0 5px 20px rgba(0, 0, 0, 0.08);
      padding: 25px;
      height: fit-content;
    }
    
    .left-panel {
      flex: 0 0 380px;
      display: flex;
      flex-direction: column;
    }
    
    .right-panel {
      flex: 1;
      display: flex;
      flex-direction: column;
    }
    
    .panel-title {
      font-size: 1.4rem;
      margin-bottom: 20px;
      padding-bottom: 15px;
      border-bottom: 2px solid #eaeaea;
      color: #2c3e50;
      display: flex;
      align-items: center;
      gap: 10px;
      height: 50px;
    }
    
    .panel-title i {
      color: #3498db;
    }
    
    .btn {
      background: linear-gradient(to bottom, #3498db, #2980b9);
      color: white;
      border: none;
      padding: 12px 20px;
      font-size: 16px;
      border-radius: 6px;
      cursor: pointer;
      transition: all 0.3s ease;
      display: inline-flex;
      align-items: center;
      gap: 8px;
      box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
    }
    
    .btn:hover {
      background: linear-gradient(to bottom, #2980b9, #2573a7);
      transform: translateY(-2px);
      box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
    }
    
    .btn:disabled {
      background: #bdc3c7;
      cursor: not-allowed;
      transform: none;
      box-shadow: none;
    }
    
    .btn-secondary {
      background: linear-gradient(to bottom, #95a5a6, #7f8c8d);
    }
    
    .btn-secondary:hover {
      background: linear-gradient(to bottom, #7f8c8d, #6c7a7d);
    }
    
    .btn-danger {
      background: linear-gradient(to bottom, #e74c3c, #c0392b);
    }
    
    .btn-danger:hover {
      background: linear-gradient(to bottom, #c0392b, #a23526);
    }
    
    .btn-success {
      background: linear-gradient(to bottom, #2ecc71, #27ae60);
    }
    
    .btn-success:hover {
      background: linear-gradient(to bottom, #27ae60, #219653);
    }
    
    .file-input-container {
      margin-bottom: 0px;
    }
    
    .file-list {
      border: 1px solid #e0e0e0;
      border-radius: 8px;
      overflow: hidden;
      margin: 20px 0;
      flex: 1;
    }
    
    .file-list-header {
      display: flex;
      background: #f8f9fa;
      padding: 12px 15px;
      font-weight: bold;
      border-bottom: 1px solid #e0e0e0;
    }
    
    .file-list-header div:first-child {
      flex: 1;
    }
    
    .file-list-header div {
      width: 100px;
      text-align: center;
    }
    
    .file-list-body {
      width: 473.86px;
      height: 370px;
      max-height: 370px;
      overflow-y: scroll;
    }
    
    .file-item {
      display: flex;
      padding: 12px 15px;
      border-bottom: 1px solid #eee;
      align-items: center;
      transition: background 0.2s;
    }
    
    .file-item:hover {
      background: #f8f9ff;
    }
    
    .file-item:last-child {
      border-bottom: none;
    }
    
    .file-name {
      flex: 1;
      width:250px
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      padding-right: 10px;
    }
    
    .file-actions {
      display: flex;
      gap: 10px;
      width: 100px;
      justify-content: center;
    }
    
    .file-action-btn {
      background: none;
      border: none;
      color: #7f8c8d;
      cursor: pointer;
      font-size: 16px;
      transition: color 0.2s;
    }
    
    .file-action-btn:hover {
      color: #e74c3c;
    }
    
    .double-print-option {
      width: 100px;
      text-align: center;
    }
    
    .double-print-toggle {
      position: relative;
      display: inline-block;
      width: 50px;
      height: 24px;
    }
    
    .double-print-toggle input {
      opacity: 0;
      width: 0;
      height: 0;
    }
    
    .toggle-slider {
      position: absolute;
      cursor: pointer;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background-color: #ccc;
      transition: .4s;
      border-radius: 24px;
    }
    
    .toggle-slider:before {
      position: absolute;
      content: "";
      height: 18px;
      width: 18px;
      left: 3px;
      bottom: 3px;
      background-color: white;
      transition: .4s;
      border-radius: 50%;
    }
    
    input:checked + .toggle-slider {
      background-color: #3498db;
    }
    
    input:checked + .toggle-slider:before {
      transform: translateX(26px);
    }
    
    .pagination {
      display: flex;
      justify-content: center;
      margin-top: 15px;
      gap: 10px;
    }
    
    .pagination-btn {
      width: 35px;
      height: 35px;
      border-radius: 50%;
      border: 1px solid #ddd;
      background: white;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      transition: all 0.2s;
    }
    
    .pagination-btn:hover, .pagination-btn.active {
      background: #3498db;
      color: white;
      border-color: #3498db;
    }
    
    .preview-container {
      flex: 1;
      display: flex;
      flex-direction: column;
      justify-content: center; /* 添加垂直居中 */
      align-items: center; /* 添加水平居中 */
      height: calc(100vh - 557.06px); /* 根据实际布局调整 */
      min-height: 557.06px; /* 最小高度保证 */
    }
    
    .preview-header {
      display: flex;
      gap: 15px;
      margin-bottom: 20px;
      flex-wrap: wrap;
      
    }
    
    #viewer {
  flex: 1;
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  overflow: auto;
  background: #f8f9fa;
  box-shadow: inset 0 1px 3px rgba(0,0,0,0.05);
  font-family: "Arial Unicode MS", "Noto Sans CJK", "Microsoft YaHei", sans-serif, "SimSun", "KaiTi","Courier New" ;
   }
    
    #viewer iframe {
      width: 100%;
      height: calc(100vh - 557.06px); /* 根据实际布局调整 */
      min-height: 557.06px; /* 最小高度保证 */
      border: none;
    }
    
    #msg {
      padding: 15px;
      background: #e3f2fd;
      border-radius: 6px;
      margin: 20px 0;
      border-left: 4px solid #3498db;
    }
    
    .stats {
      display: flex;
      gap: 20px;
      margin-top: 20px;
      padding-top: 20px;
      border-top: 1px solid #eee;
    }
    
    .stat-card {
      flex: 1;
      background: #f8f9fa;
      padding: 15px;
      border-radius: 8px;
      text-align: center;
    }
    
    .stat-value {
      font-size: 1.8rem;
      font-weight: bold;
      color: #3498db;
      margin: 10px 0;
    }
    
    .stat-label {
      color: #7f8c8d;
      font-size: 0.9rem;
    }
    
    .instructions {
      background: #e8f4fd;
      padding: 15px;
      border-radius: 8px;
      margin-top: 20px;
      font-size: 0.9rem;
    }
    
    .instructions h3 {
      margin-bottom: 10px;
      color: #2c7bbe;
    }
    
    .instructions ul {
      padding-left: 20px;
    }
    
    .instructions li {
      margin-bottom: 8px;
    }
    
    .feature-badge {
      display: inline-block;
      background: #3498db;
      color: white;
      padding: 3px 8px;
      border-radius: 4px;
      font-size: 0.8rem;
      margin-left: 5px;
    }
    
    @media (max-width: 1200px) {
      .container {
        flex-direction: column;
      }
      
      .left-panel {
        flex: 1;
        margin-bottom: 20px;
      }
    }
  </style>
</head>
<body>
  <header>
    <p><i class="fas fa-file-invoice"></i> 数电发票批量合并打印工具</p>
  </header>
  
  <div class="container">
    <!-- 左侧文件管理面板 -->
    <div class="left-panel panel">
      <h2 class="panel-title"><i class="fas fa-file-import"></i> 发票文件管理（单次最多导入50条记录）</h2>
      
      <div class="file-input-container">
        <input type="file" id="pdfFiles" accept="application/pdf" multiple class="btn" style="display: none;">
        <button id="selectFilesBtn" class="btn"><i class="fas fa-folder-open"></i> 选择发票文件</button>
        <button id="clearFilesBtn" class="btn btn-danger" onclick="location.reload()"><i class="fas fa-trash"></i> 重置</button>
        <button id="processBtn" class="btn"><i class="fas fa-cogs"></i> 合并并预览</button>

        <div class="global-double-print">
          <label style="display: inline ; align-items: center;gap: 10px; cursor: pointer;">
            <input type="checkbox" id="globalDoublePrint" style="width: 20px; height: 20px;">
            全局启用双打功能
          </label>
          &nbsp;&nbsp;&nbsp;&nbsp; 纸张规格:
          <select id="paperType" style="font-size: 16px;">
            <option value="A4">A4</option>
            <option value="A5">A5</option>
          </select>
               
        </div>
      </div>
      
      <div class="file-list">
        <div class="file-list-header">
          <div>文件名</div>
          <div>双打</div>
          <div>操作</div>
        </div>
        <div class="file-list-body" id="fileListBody">
          <!-- 文件列表将通过JS动态生成 -->
          <div class="no-files" style="text-align: center; padding: 40px 20px; color: #95a5a6;">
            <i class="fas fa-file-pdf" style="font-size: 3rem; margin-bottom: 15px;"></i>
            <p>请添加发票PDF文件</p>
          </div>
        </div>
      </div>
      
      <div class="pagination" id="pagination"></div>
  
      <div class="instructions">
        <h3><i class="fas fa-info-circle"></i> 使用说明</h3>
        <ul>
          <li>单次最多导入50条发票</li>
          <li>项目明细区（含规格型号）不超过8行（即pdf文档高度不超过14cm）视为短票，超过的视为长票</li>
          <li>A4模式下，2张短票将自动合并1页打印，长票将单独打印1页</li>
          <li>A5模式下，短票单独打印1页，长票过滤不打印</li>
          <li>开启"双打"选项，A4模式下，短票在1张纸上打印2份，长票打印2张纸，A5模式下，短票打印2张纸，长票过滤不打印</li>
        </ul>
      </div>
    </div>
    
    <!-- 右侧预览面板 -->
    <div class="right-panel panel">
      <h2 class="panel-title"><i class="fas fa-print"></i> 预览区域</h2>
      <div id="msg">等待处理发票文件...</div>
      <div class="preview-container">
        <div id="viewer">
          <div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: #95a5a6;">
            <i class="fas fa-file-pdf" style="font-size: 4rem; margin-bottom: 20px;"></i>
            <h3>预览区域</h3>
            <p>合并后的发票将在此处显示</p>
          </div>
        </div>
      </div>
      <div class="stats">
        <div class="stat-card">
          <div class="stat-label">已加载发票</div>
          <div class="stat-value" id="totalFiles">0</div>
          <div>PDF文件</div>
        </div>
        <div class="stat-card">
          <div class="stat-label">双打发票</div>
          <div class="stat-value" id="doublePrintCount">0</div>
          <div>已启用</div>
        </div>
      </div>
    </div>
  </div>

  <script>
    // pdf.js设置
    pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
    pdfjsLib.cMapUrl = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@3.11.174/cmaps/';
    pdfjsLib.cMapPacked = true;
    pdfjsLib.fontExtraProperties = {standardFontDataUrl: 'https://cdn.jsdelivr.net/npm/pdfjs-dist@3.11.174/standard_fonts/'};

    // 纸张规格
    const PAPER_SPECS = {
      A4: { width: 595.28, height: 841.89 },
      A5: { width: 595.28, height: 419.53 }
    };
    let paperType = 'A4';

    // 常量
    const CM_TO_PT = 28.3465;
    const THRESHOLD_CM = 14.01;
    const THRESHOLD_PT = THRESHOLD_CM * CM_TO_PT;
    const RENDER_SCALE = 3.0;
    const DPI = 300;

    // 交互变量
    let docUrl = null;
    let uploadedFiles = [];
    let currentPage = 1;
    const itemsPerPage = 20;

    document.getElementById('paperType').addEventListener('change', function(){
      paperType = this.value;
      document.getElementById('paperNote').textContent =
        paperType === 'A5'
          ? 'A5模式下仅打印短票，长票将自动跳过，每张短票各占一张A5纸。'
          : '';
    });

    // 文件选择
    document.getElementById('selectFilesBtn').addEventListener('click', () => {
      document.getElementById('pdfFiles').click();
    });

    document.getElementById('pdfFiles').addEventListener('change', function(e) {
      const files = Array.from(e.target.files);
      if (files.length + uploadedFiles.length > 50) {
        alert("最多只能上传50个文件！");
        this.value = '';
        return;
      }
      
function isFileDuplicate(newFile) {
  return uploadedFiles.some(existingFile => 
    existingFile.name === newFile.name
  );
}
 // 新增重复文件检查
  const duplicateFiles = files.filter(file => isFileDuplicate(file));
  if(duplicateFiles.length > 0) {
    alert(`以下文件已存在: ${duplicateFiles.map(f => f.name).join(', ')}`);
    return;
  }

      
      
      files.forEach(file => {
        uploadedFiles.push({
          file: file,
          name: file.name,
          doublePrint: false,
          isShort: null,
          pages: 0
        });
      });
      renderFileList();
      updateStats();
    });

    // 全局双打
    document.getElementById('globalDoublePrint').addEventListener('change', function(e) {
      const isChecked = e.target.checked;
      uploadedFiles.forEach(file => {
        if (file) file.doublePrint = isChecked;
      });
      renderFileList();
      updateStats();
    });

    // 文件列表渲染
    function renderFileList() {
      const fileListBody = document.getElementById('fileListBody');
      const pagination = document.getElementById('pagination');
      fileListBody.innerHTML = '';
      const totalPages = Math.ceil(uploadedFiles.length / itemsPerPage);
      const startIndex = (currentPage - 1) * itemsPerPage;
      const endIndex = Math.min(startIndex + itemsPerPage, uploadedFiles.length);
      const currentFiles = uploadedFiles.slice(startIndex, endIndex);

      if (currentFiles.length === 0) {
        fileListBody.innerHTML = `
          <div class="no-files" style="text-align: center; padding: 40px 20px; color: #95a5a6;">
            <i class="fas fa-file-pdf" style="font-size: 3rem; margin-bottom: 15px;"></i>
            <p>请添加发票PDF文件</p>
          </div>
        `;
      } else {
        currentFiles.forEach((file, index) => {
          const globalIndex = startIndex + index;
          let typeText = '';
          if (file.isShort === true) typeText = '<span style="margin-left:8px;color:#27ae60;font-size:12px;">短票</span>';
          else if (file.isShort === false) typeText = `<span style="margin-left:8px;color:#e67e22;font-size:12px;">长票</span>`;
          fileListBody.innerHTML += `
            <div class="file-item">
              <div class="file-name" title="${file.name}">
                <i class="fas fa-file-pdf" style="color: #e74c3c; margin-right: 8px;"></i>
                ${globalIndex + 1}. ${file.name} ${typeText}
              </div>
              <div class="double-print-option">
                <label class="double-print-toggle">
                  <input type="checkbox" ${file.doublePrint ? 'checked' : ''} 
                    ${(file.isShort === false && paperType === 'A4') ? 'disabled' : ''} 
                    data-index="${globalIndex}">
                  <span class="toggle-slider"></span>
                </label>
              </div>
              <div class="file-actions">
                <button class="file-action-btn delete-btn" data-index="${globalIndex}">
                  <i class="fas fa-trash-alt"></i>
                </button>
              </div>
            </div>
          `;
        });
      }
      // 分页
      pagination.innerHTML = '';
      if (totalPages > 1) {
        for (let i = 1; i <= totalPages; i++) {
          const pageBtn = document.createElement('div');
          pageBtn.className = `pagination-btn ${i === currentPage ? 'active' : ''}`;
          pageBtn.textContent = i;
          pageBtn.addEventListener('click', () => { currentPage = i; renderFileList(); });
          pagination.appendChild(pageBtn);
        }
      }
      // 删除
      document.querySelectorAll('.delete-btn').forEach(btn => {
        btn.addEventListener('click', function() {
          const index = parseInt(this.getAttribute('data-index'));
          uploadedFiles.splice(index, 1);
          renderFileList();
          updateStats();
        });
      });
      // 双打切换
      document.querySelectorAll('.double-print-option input').forEach(checkbox => {
        checkbox.addEventListener('change', function() {
          const index = parseInt(this.getAttribute('data-index'));
          uploadedFiles[index].doublePrint = this.checked;
          updateStats();
        });
      });
    }

    function updateStats() {
      document.getElementById('totalFiles').textContent = uploadedFiles.length;
      const doublePrintCount = uploadedFiles.filter(file => file.doublePrint).length;
      document.getElementById('doublePrintCount').textContent = doublePrintCount;
      // 预估页数
      let estimatedPages = 0;
      if (paperType === 'A4') {
        estimatedPages = Math.ceil(uploadedFiles.length / 2) + doublePrintCount;
      } else {
        // A5模式：只算短票，双打算两页
        estimatedPages = uploadedFiles.reduce((sum, file) => {
          if (file.isShort === true) return sum + (file.doublePrint ? 2 : 1);
          return sum;
        }, 0);
      }
      document.getElementById('estimatedPages').textContent = estimatedPages;
    }

    // 合并&预览主逻辑
    document.getElementById('processBtn').onclick = async function() {
      if (uploadedFiles.length === 0) { 
        alert("请先选择PDF文件！"); 
        return; 
      }
      document.getElementById('processBtn').disabled = true;
      document.getElementById('msg').textContent = "正在处理发票，请稍候...";

      const {width: PAGE_WIDTH, height: PAGE_HEIGHT} = PAPER_SPECS[paperType];
      const CANVAS_WIDTH = Math.round(PAGE_WIDTH * RENDER_SCALE);
      const CANVAS_HEIGHT = Math.round(PAGE_HEIGHT * RENDER_SCALE);

      let shortPages = [];
      let longPages = [];
      let skippedCount = 0;
      let processedCount = 0;

      // 扫描文件
      for(let fileIdx = 0; fileIdx < uploadedFiles.length; fileIdx++) {
        const fileData = uploadedFiles[fileIdx];
        const file = fileData.file;
        const arrayBuffer = await file.arrayBuffer();
        const pdf = await pdfjsLib.getDocument({data: arrayBuffer, cMapUrl: pdfjsLib.cMapUrl, cMapPacked: true, disableFontFace: false}).promise;
        fileData.pages = pdf.numPages;
        fileData.isShort = null;
        for(let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
          const page = await pdf.getPage(pageNum);
          const vp = page.getViewport({scale: 1.0});
          if (vp.height <= THRESHOLD_PT) {
            fileData.isShort = true;
            shortPages.push({ page, vp, fileIdx, pageNum, pdf, doublePrint: fileData.doublePrint });
          } else {
            fileData.isShort = false;
            if (paperType === 'A4') {
              longPages.push({ page, vp, fileIdx, pageNum, pdf, doublePrint: fileData.doublePrint });
            } else if (paperType === 'A5') {
              skippedCount++;
            }
          }
        }
        processedCount++;
        document.getElementById('msg').textContent = `正在处理发票，请稍候...（${processedCount}/${uploadedFiles.length}）`;
      }
      renderFileList();

      async function renderPage2Img(page, vp) {
        const scale = Math.min(RENDER_SCALE * (PAGE_WIDTH / vp.width), RENDER_SCALE);
        const viewport = page.getViewport({ scale, dontThrow: true, intent: 'print',
        fontExtraProperties: {
      disableFontFace: false  // 确保启用字体加载
    }
         });
        const canvas = document.createElement('canvas');
        canvas.width = Math.round(viewport.width);
        canvas.height = Math.round(viewport.height);
        const ctx = canvas.getContext('2d');
        ctx.imageSmoothingEnabled = true;
        ctx.imageSmoothingQuality = 'high';
        await page.render({canvasContext: ctx, viewport}).promise;
        return canvas;
      }

      let outputPages = [];
      if (paperType === 'A4') {
        // 短票（最多两票一页）
        let shortArr = [];
        shortPages.forEach(info => {
          shortArr.push(info);
          if (info.doublePrint) shortArr.push({...info});
        });
        for(let i = 0; i < shortArr.length; i += 2) {
          const cA4 = document.createElement('canvas');
          cA4.width = CANVAS_WIDTH;
          cA4.height = CANVAS_HEIGHT;
          const ctx = cA4.getContext('2d');
          ctx.fillStyle = "#fff"; 
          ctx.fillRect(0, 0, cA4.width, cA4.height);
          const page1 = shortArr[i];
          const c1 = await renderPage2Img(page1.page, page1.vp);
          let c2 = null;
          if (i+1 < shortArr.length) {
            let page2 = shortArr[i+1];
            c2 = await renderPage2Img(page2.page, page2.vp);
          }
          const gap = Math.round(40 * RENDER_SCALE);
          let totalHeight = c1.height + (c2 ? c2.height : 0) + (c2 ? gap : 0);
          let scaleFactor = 1.0;
          if(totalHeight > CANVAS_HEIGHT - gap) scaleFactor = (CANVAS_HEIGHT - gap) / totalHeight;
          let yPos = Math.round(30 * RENDER_SCALE);
          ctx.drawImage(
            c1, (CANVAS_WIDTH - c1.width * scaleFactor) / 2, 
            yPos,
            c1.width * scaleFactor, c1.height * scaleFactor
          );
          if(c2) {
            const lineY = yPos + c1.height * scaleFactor + Math.round(gap / 2);
            ctx.save();
            ctx.strokeStyle = '#525659';
            ctx.lineWidth = 2 * RENDER_SCALE;
            ctx.setLineDash([5 * RENDER_SCALE, 3 * RENDER_SCALE]);
            ctx.beginPath();
            ctx.moveTo(60 * RENDER_SCALE, lineY);
            ctx.lineTo(CANVAS_WIDTH - 60 * RENDER_SCALE, lineY);
            ctx.stroke();
            ctx.setLineDash([]);
            ctx.restore();
            yPos += c1.height * scaleFactor + gap;
            ctx.drawImage(
              c2, (CANVAS_WIDTH - c2.width * scaleFactor) / 2, 
              yPos, c2.width * scaleFactor, c2.height * scaleFactor
            );
          }
          outputPages.push(cA4);
        }
        // 长票单独A4一页
        for (let item of longPages) {
          const cA4 = document.createElement('canvas');
          cA4.width = CANVAS_WIDTH; 
          cA4.height = CANVAS_HEIGHT;
          const ctx = cA4.getContext('2d');
          ctx.fillStyle = "#fff"; 
          ctx.fillRect(0, 0, cA4.width, cA4.height);
          const c1 = await renderPage2Img(item.page, item.vp);
          const scale = Math.min(
            (CANVAS_WIDTH - 80 * RENDER_SCALE) / c1.width, 
            (CANVAS_HEIGHT - 80 * RENDER_SCALE) / c1.height, 1
          );
          const w = c1.width * scale;
          const h = c1.height * scale;
          ctx.drawImage(
            c1, (CANVAS_WIDTH - w) / 2, 
            (CANVAS_HEIGHT - h) / 2, w, h
          );
          outputPages.push(cA4);
          if (item.doublePrint) {
            const cA4_2 = document.createElement('canvas');
            cA4_2.width = CANVAS_WIDTH; 
            cA4_2.height = CANVAS_HEIGHT;
            const ctx2 = cA4_2.getContext('2d');
            ctx2.fillStyle = "#fff"; 
            ctx2.fillRect(0, 0, cA4_2.width, cA4_2.height);
            ctx2.drawImage(
              c1, (CANVAS_WIDTH - w) / 2, 
              (CANVAS_HEIGHT - h) / 2, w, h
            );
            outputPages.push(cA4_2);
          }
        }
      } else if (paperType === 'A5') {
        // 只处理短票，每票单独A5一页，双打时重复两次
        for (let pageInfo of shortPages) {
          const repeat = pageInfo.doublePrint ? 2 : 1;
          const c1 = await renderPage2Img(pageInfo.page, pageInfo.vp);
          for (let i = 0; i < repeat; i++) {
            const cA5 = document.createElement('canvas');
            cA5.width = CANVAS_WIDTH;
            cA5.height = CANVAS_HEIGHT;
            const ctx = cA5.getContext('2d');
            ctx.fillStyle = "#fff"; 
            ctx.fillRect(0, 0, cA5.width, cA5.height);
            // 缩放居中
            const scale = Math.min(
              (CANVAS_WIDTH - 40*RENDER_SCALE) / c1.width, 
              (CANVAS_HEIGHT - 40*RENDER_SCALE) / c1.height, 1
            );
            const drawW = c1.width * scale;
            const drawH = c1.height * scale;
            ctx.drawImage(
              c1,
              (CANVAS_WIDTH - drawW) / 2,
              (CANVAS_HEIGHT - drawH) / 2,
              drawW,
              drawH
            );
            outputPages.push(cA5);
          }
        }
      }
      // 生成PDF
      const { jsPDF } = window.jspdf;
      const pdfDoc = new jsPDF({unit: 'pt',
       format: [PAGE_WIDTH, PAGE_HEIGHT],
  orientation: PAGE_WIDTH > PAGE_HEIGHT ? 'landscape' : 'portrait',compress: true
       });
      for(let i = 0; i < outputPages.length; i++) {
        const canvas = outputPages[i];
        const imgData = canvas.toDataURL('image/png', 1.0);
        if(i > 0) pdfDoc.addPage();
        pdfDoc.addImage(imgData, 'PNG', 0, 0, PAGE_WIDTH, PAGE_HEIGHT);
      }
      const pdfBlob = pdfDoc.output('blob');
      if(docUrl) URL.revokeObjectURL(docUrl);
      docUrl = URL.createObjectURL(pdfBlob);
      document.getElementById('viewer').innerHTML = `
          <iframe src="${docUrl}" width="100%" height="100%"></iframe>
      `;
      document.getElementById('msg').textContent =
        outputPages.length === 0
          ? '没有可打印的短票，A5模式下长票已自动跳过。'
          : `合并处理完成！等待预览区加载完成后即可打印或导出，共生成${outputPages.length}页，${skippedCount ? `有${skippedCount}张长票被跳过。` : ''}`;
      document.getElementById('processBtn').disabled = false;
      document.getElementById('estimatedPages').textContent = outputPages.length;
    };
  </script>
</body>
</html>
